﻿using Abp.Domain.Entities;
using Dapper;
using Dapper.Contrib.Extensions;
using Mall.DomainModel.Dto.Query;
using Microsoft.Data.SqlClient;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Mall.Repository.Repository
{
    public class DapperRepositoryBase<TEntity, TPrimaryKey> : AbpRepositoryBase<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey>
    {
        protected IDapperConfiguration Configuration { get; set; }
        protected IDbConnection WriteConnection { get; set; }  // 写入连接
        protected IDbConnection ReadConnection { get; set; }   // 读取连接

        protected bool UseOriginalTableName { get; set; }
        public DapperRepositoryBase(IDapperConfiguration configuration)
        {
            Configuration = configuration;
            WriteConnection = new SqlConnection(Configuration.WriteConnectionString);
            ReadConnection = new SqlConnection(Configuration.ReadConnectionString);
        }
        // 带表名控制的构造函数（可选）
        public DapperRepositoryBase(IDapperConfiguration configuration, bool isOriginalTableName)
        : this(configuration)
        {
            UseOriginalTableName = isOriginalTableName;
        }
        public override Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
        {
            using (ReadConnection)
            {
                return ReadConnection.GetAsync<TEntity>(id);
            }
        }

        public override Task<IEnumerable<TEntity>> GetAllListAsync()
        {
            using (ReadConnection)
            {
                return ReadConnection.GetAllAsync<TEntity>();
            }
        }

        public override Task<TEntity> GetAsync(TPrimaryKey id)
        {
            using (ReadConnection)
            {
                return ReadConnection.GetAsync<TEntity>(id);
            }
        }

        public override async Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity)
        {
            using (WriteConnection)
            {
                await WriteConnection.InsertAsync(entity);
                return entity.Id;
            }
        }

        public override async Task<TEntity> InsertAsync(TEntity entity)
        {
            using (WriteConnection)
            {
                await WriteConnection.InsertAsync(entity);
                return entity;
            }
        }

        public override async Task<TEntity> UpdateAsync(TEntity entity)
        {
            // 创建新连接实例（不能直接复用类字段的connection）
            using (var connection = new SqlConnection(Configuration.WriteConnectionString))
            {
                await connection.OpenAsync();
                bool isUpdated = await connection.UpdateAsync(entity);
                return isUpdated ? entity : null;
            }
        }
        public override async Task<int> CountAsync(IQueryObject<TEntity> query)
        {
            DynamicParameters dynamicParameters = new DynamicParameters();
            ExpressionToSqlmpl<TEntity> expressionToSqlmpl = ExpressionHelper.Count<TEntity>();
            foreach (Expression<Func<TEntity, bool>> item in query.QueryExpression)
            {
                expressionToSqlmpl.Where(item);
            }

            foreach (KeyValuePair<string, object> dbParam in expressionToSqlmpl.DbParams)
            {
                dynamicParameters.Add(dbParam.Key, dbParam.Value);
            }

            using (ReadConnection)
            {
                return await ReadConnection.QuerySingleAsync<int>(expressionToSqlmpl.Sql, dynamicParameters);
            }
        }

        public override async Task<Tuple<int, IList<TEntity>>> GetListAsync(IQueryObject<TEntity> query)
        {
            DynamicParameters parameters = new DynamicParameters();
            ExpressionToSqlmpl<TEntity> expressionToSqlmpl = ExpressionHelper.Select<TEntity>();
            ExpressionToSqlmpl<TEntity> count = ExpressionHelper.Count<TEntity>();
            foreach (Expression<Func<TEntity, bool>> item2 in query.QueryExpression)
            {
                expressionToSqlmpl.Where(item2);
            }

            foreach (Expression<Func<TEntity, bool>> item3 in query.QueryExpression)
            {
                count.Where(item3);
            }

            if ((int)query.OrderSort == (int)SortOrder.ASC)
            {
                expressionToSqlmpl.OrderBy(query.OrderField);
            }
            else if ((int)query.OrderSort == (int)SortOrder.DESC)
            {
                expressionToSqlmpl.OrderByDesc(query.OrderField);
            }

            if (query.Count >= 0)
            {
                expressionToSqlmpl.Limit(query.Skip, query.Count);
            }

            foreach (KeyValuePair<string, object> dbParam in expressionToSqlmpl.DbParams)
            {
                parameters.Add(dbParam.Key, dbParam.Value);
            }

            using (ReadConnection)
            {
                return new Tuple<int, IList<TEntity>>(item2: (await ReadConnection.QueryAsync<TEntity>(expressionToSqlmpl.Sql, parameters)).ToList(), item1: await ReadConnection.QuerySingleAsync<int>(count.Sql, parameters));
            }
        }

        protected string CreateInertSql<T>(string tbName)
        {
            List<string> list = new List<string>();
            PropertyInfo[] properties = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                TypeInfo typeInfo = propertyInfo.PropertyType.GetTypeInfo();
                if (!typeInfo.IsInterface && (!typeInfo.IsClass || typeInfo.IsSealed))
                {
                    list.Add(propertyInfo.Name);
                }
            }

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append($"INSERT INTO {tbName}(");
            for (int j = 0; j < list.Count; j++)
            {
                if (j == 0)
                {
                    stringBuilder.Append("`" + list[j] + "`");
                }
                else
                {
                    stringBuilder.Append($",`{list[j]}`");
                }
            }

            stringBuilder.Append(") VALUES(");
            for (int k = 0; k < list.Count; k++)
            {
                string text = "";
                if (k != 0)
                {
                    text = ",";
                }

                string value = string.Format(text + "@{0}", list[k]);
                stringBuilder.Append(value);
            }

            stringBuilder.Append(") ");
            return stringBuilder.ToString();
        }

        protected DynamicParameters CreateParameters<T>(T t)
        {
            DynamicParameters dynamicParameters = new DynamicParameters();
            PropertyInfo[] properties = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                TypeInfo typeInfo = propertyInfo.PropertyType.GetTypeInfo();
                if (!typeInfo.IsInterface && (!typeInfo.IsClass || typeInfo.IsSealed))
                {
                    dynamicParameters.Add("@" + propertyInfo.Name, propertyInfo.GetValue(t));
                }
            }

            return dynamicParameters;
        }

        protected string UpdateSql<T>(string tbName, string keyName)
        {
            List<string> list = new List<string>();
            PropertyInfo[] properties = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                if (!propertyInfo.PropertyType.GetTypeInfo().IsInterface && (!propertyInfo.PropertyType.GetTypeInfo().IsClass || propertyInfo.PropertyType.GetTypeInfo().IsSealed))
                {
                    list.Add(propertyInfo.Name);
                }
            }

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append($"UPDATE {tbName} SET ");
            bool flag = true;
            for (int j = 0; j < list.Count; j++)
            {
                if (list[j] != keyName)
                {
                    if (flag)
                    {
                        stringBuilder.Append(string.Format("{0}=@{0}", list[j]));
                        flag = false;
                    }
                    else
                    {
                        stringBuilder.Append(string.Format(",{0}=@{0}", list[j]));
                    }
                }
            }

            stringBuilder.Append(string.Format(" WHERE {0}=@{0};", keyName));
            return stringBuilder.ToString();
        }

        protected string UpdateSql(string tbName, string keyName, DynamicParameters param)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append($"UPDATE {tbName} SET ");
            for (int i = 0; i < param.ParameterNames.Count(); i++)
            {
                if (param.ParameterNames.ElementAt(i) != keyName)
                {
                    if (i == 0)
                    {
                        stringBuilder.Append(string.Format("{0}=@{0}", param.ParameterNames.ElementAt(i)));
                    }
                    else
                    {
                        stringBuilder.Append(string.Format(",{0}=@{0}", param.ParameterNames.ElementAt(i)));
                    }
                }
            }

            stringBuilder.Append(string.Format(" WHERE {0}=@{0};", keyName));
            return stringBuilder.ToString();
        }

        protected string ParamsUpdateSql(List<string> paramNames)
        {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < paramNames.Count(); i++)
            {
                if (i == 0)
                {
                    stringBuilder.Append(string.Format("{0}=@{0}", paramNames[i]));
                }
                else
                {
                    stringBuilder.Append(string.Format(",{0}=@{0}", paramNames[i]));
                }
            }

            return stringBuilder.ToString();
        }

        protected DynamicParameters CreateParameters<T>(T t, List<string> paramNames)
        {
            DynamicParameters dynamicParameters = new DynamicParameters();
            PropertyInfo[] properties = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in properties)
            {
                if (paramNames.Contains(propertyInfo.Name))
                {
                    dynamicParameters.Add("@" + propertyInfo.Name, propertyInfo.GetValue(t));
                }
            }

            return dynamicParameters;
        }
    }
    public enum SortOrder
    {
        None,
        ASC,
        DESC
    }

}
